package io.gitee.buzizhi.tools;

import io.gitee.buzizhi.relation.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

public class AES128Tool {

    /**
     * <li> 加密方法</li>
     * @param data  要加密的数据
     * @param key 加密key
     * @param iv 加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) {
        parameterVerification(data,key,iv);
        // 判断Key是否正确
        if (key == null) {
            throw new IllegalArgumentException("解密key不能为空！");
        }
        if (key.length() != 16) {
            throw new IllegalArgumentException("解密key必须为16位！");
        }
        // 判断补充密码是否正确
        if (iv == null) {
            throw new IllegalArgumentException("补充密码不能为空！");
        }
        if (iv.length() != 16) {
            throw new IllegalArgumentException("补充密码必须为16位！");
        }
        try {
            //"算法/模式/补码方式"NoPadding PkcsPadding
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            int blockSize = cipher.getBlockSize();

            byte[] dataBytes = data.getBytes("utf-8");
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            return new Base64().encodeToString(encrypted);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * <li> 解密</li>
     * @param data 解密的数据
     * @param key 解密key
     * @param iv 解密iv
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws UnsupportedEncodingException
     */
    public static String decrypt(String data, String key, String iv){
        // 判断Key是否正确
        if (key == null) {
            throw new IllegalArgumentException("解密key不能为空！");
        }
        if (key.length() != 16) {
            throw new IllegalArgumentException("解密key必须为16位！");
        }
        // 判断补充密码是否正确
        if (iv == null) {
            throw new IllegalArgumentException("补充密码不能为空！");
        }
        if (iv.length() != 16) {
            throw new IllegalArgumentException("补充密码必须为16位！");
        }
        try{
            parameterVerification(data,key,iv);
            byte[] encrypted1 = new Base64().decode(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original,"utf-8");
            return AsciiToChineseString(originalString);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    private static void parameterVerification(String data,String key,String iv){
        parameterVerification(data,key);
        if(iv == null){
            throw new IllegalArgumentException("加密IV为空！");
        }
    }

    private static void parameterVerification(String data,String key){
        if(data == null){
            throw new IllegalArgumentException("加密数据为空！");
        }
        if(key == null){
            throw new IllegalArgumentException("加密Key为空！");
        }
    }

    public static String AsciiToChineseString ( String s ) {
        Charset charset = Charset.forName("UTF-8");
        ByteBuffer bb = charset.encode(s);
        byte[] b = bb.array();
        for (int i = 0; i < b.length; i++) {
            byte c = b[i];
        }
        CharBuffer cb = charset.decode(bb);
        return cb.toString();
    }


    /**
     * <li> 加密</li>
     * @param data 待加密数据
     * @param key 加密key
     * @return
     * @throws Exception
     */
    public static String encrypt(String data, String key) throws Exception {
        parameterVerification(data,key);
        // 判断Key是否正确
        if (key == null) {
            throw new IllegalArgumentException("解密key不能为空！");
        }
        // 判断Key是否为16位
        if (key.length() != 16) {
            throw new IllegalArgumentException("解密key必须为16位！");
        }
        byte[] raw = key.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(data.getBytes("utf-8"));
        //此处使用BASE64做转码功能，同时能起到2次加密的作用。
        Base64 base64 = new Base64();
        return base64.encodeToString(encrypted);
    }

    /**
     * <li> 解密</li>
     * @param data 待解密数据
     * @param key 解密key
     * @return
     * @throws Exception
     */
    public static String decrypt(String data, String key) throws Exception {
        parameterVerification(data,key);
        try {
            // 判断Key是否正确
            if (key == null) {
                throw new IllegalArgumentException("解密key不能为空！");
            }
            // 判断Key是否为16位
            if (key.length() != 16) {
                throw new IllegalArgumentException("解密key必须为16位！");
            }
            Base64 base64 = new Base64();
            byte[] raw = key.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = base64.decode(data);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, "utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    /**
     * <li> 生成密钥（盐）</li>
     * @param n 密钥长度
     * @return
     */
    public static String keyGenerate(int n) {
        String val = "";
        Random random = new Random();
        for (int i = 0; i < n; i++) {
            String str = random.nextInt(2) % 2 == 0 ? "num" : "char";
            if ("char".equalsIgnoreCase(str)) { // 产生字母
                int nextInt = random.nextInt(2) % 2 == 0 ? 65 : 97;
                // System.out.println(nextInt + "!!!!"); 1,0,1,1,1,0,0
                val += (char) (nextInt + random.nextInt(26));
            } else if ("num".equalsIgnoreCase(str)) { // 产生数字
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }

}
